home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / kruse_11.zip / 11SOL.EXT < prev    next >
Text File  |  1990-12-01  |  18KB  |  582 lines

  1.  
  2. {Section 11.3  Phase 1:  Splitting the Text into Words}
  3.  
  4.  
  5. {Exercise E6}
  6.  
  7. const
  8.   nfiles        =    8;  {number of temporary files for unprocessed words}
  9. type
  10.   filecode      =  1..nfiles;
  11. var
  12.   RefFile:      array[filecode] of fileref;               
  13. {local files used for auxiliary storage of words from phase 1 to phase 2: 
  14. Normally, a separate file exist for each initial letter; this version uses 
  15. nfiles files due to operating system constraints.} 
  16.  
  17.  
  18. procedure SplitWords;
  19. var
  20.   outcount:  array[filecode] of integer;            {counter for word files}
  21.   code:  filecode;                           {Into which file does word go?}
  22. {The remainder of the local declarations are unchanged.}
  23.  
  24. begin                                          {procedure  SplitWords}
  25.   Initialize;                   {sets up files, hash table, constants}
  26.   GetWord(w);                       {Obtain a single word from InText.}
  27.   while not endinput do
  28.   begin
  29.     x := HashAddress(w);
  30.     if w <> hash[x] then
  31.     begin                            {Not in hash table; put into RefFile.}
  32.       code := FindFile( w[1] );
  33.       outcount[code] := outcount[code] + 1;
  34.       with RefFile[code]^ do               {Update the storage file.}
  35.       begin
  36.         wd := w;
  37.         pg := pagecount
  38.       end;
  39.       Put(RefFile[code])
  40.     end;
  41.     GetWord(w)
  42.   end;
  43.   Conclude                           {writes word counts to output}
  44. end;                                          {procedure SplitWords}
  45.  
  46.  
  47. function  FindFile( ch:  letter):  filecode;
  48. {Uses binary decision tree to select one of nfiles = 8 files depending
  49. on the letter ch.  }
  50. begin                           {function FindFile}
  51.   if ch < 'M' then
  52.     if ch < 'E' then
  53.       if ch < 'C' then  FindFile := 1
  54.                else  FindFile := 2
  55.     else if ch < 'H' then  FindFile := 3
  56.                  else  FindFile := 4
  57.   else if ch < 'S' then
  58.     if ch < 'P' then  FindFile := 5
  59.              else  FindFile := 6
  60.     else if ch < 'T' then  FindFile := 7
  61.                  else  FindFile := 8
  62. end;                            {function FindFile}
  63.  
  64.  
  65.     for ch := A to Z do
  66.     begin
  67.       rewrite( RefFile[ch] );
  68.       outcount[ch] := 0
  69.     end;
  70.  
  71.  
  72.     for i := 1 to nfiles do
  73.     begin
  74.       rewrite( RefFile[i] );
  75.       outcount[i] := 0
  76.     end;
  77.  
  78.  
  79. procedure Conclude;
  80. {Writes out counts of various word lists.  For some systems, it is 
  81.  necessary to close files, which should be done here.}
  82. var
  83.   i: integer;                                                 {loop index}
  84. begin                                                 {procedure Conclude}
  85.   writeln('The total number of words read in is ', wordcount:7);
  86.   writeln;
  87.   writeln('The number of words to process further in the next stage,');
  88.   writeln('in each temporary file, is below.');
  89.   writeln('a - b    c - d    e - g    h - l    m - o    p - r    s    t - z');
  90.   for i := 1 to nfiles do
  91.     write(outcount[i]:7);
  92.   writeln;
  93.   writeln
  94. end;                                                  {procedure Conclude}
  95.  
  96.  
  97.  
  98.  
  99. {Section 11.4  Phase 2:  Classifying the Words}
  100.  
  101.  
  102. {Exercise E3}
  103.  
  104. procedure ClassifyWords;
  105. {The references stored in the temporary files are placed into a list, 
  106.  the words from the file InIndex are compared with the words in the list
  107.  as they are merged into the file NewIndex.}
  108. type
  109.   wordtype  = (hash, count, index);          {ways to process a word}
  110.   pointref  = ^reflist;
  111.   reflist = record                            {list of page references}
  112.             pg:   integer;
  113.             next: pointref
  114.           end;
  115.   pointer   = ^node;
  116.   node = record                     {node of list storing words}
  117.           wd:       word;
  118.           kind:     wordtype;
  119.           ct:       integer;
  120.           ref:      pointref;
  121.           next:     pointer
  122.          end;
  123.        {Cannot use varying types as wordtype is not known upon first reading.}
  124.   list = record
  125.           head:  pointer
  126.         end;
  127. var
  128.   code:       filecode;          {index used to loop through temporary files}
  129.   NewList:    list;
  130.  
  131.  
  132. procedure Merge(p, q: pointer; var r: pointer);
  133. {Merges two sorted lists into one, that will begin at r;
  134.  requires that both lists be nonempty.  This version is modified 
  135.  slightly from the version listed in the text due to a difference 
  136.  in the data structures used.}
  137. var
  138.   s:  pointer;     {always points to last node of sorted list}
  139. begin                                  {procedure Merge}
  140.   if (p = nil) or (q = nil) then
  141.     writeln('Merge called with empty list(s).');
  142.   if p^.wd <= q^.wd then  {First find the head, r, of the merged list.}
  143.   begin                 {Note the change from .info.key to .wd. }
  144.     r := p;
  145.     p := p^.next
  146.   end
  147.   else begin
  148.     r := q;
  149.     q := q^.next
  150.   end;
  151.   s := r;       {s always points to the last entry of the merged list.}
  152.   while (p <> nil) and (q <> nil) do
  153.     if p^.wd <= q^.wd then      {Note the change from .info.key to .wd. }
  154.     begin
  155.       s^.next := p; {Attach the node with the smaller key to the sorted list.}
  156.       s := p;
  157.       p := p^.next      {Advance to the next unmerged node.}
  158.     end
  159.     else begin
  160.       s^.next := q;
  161.       s := q;
  162.       q := q^.next
  163.     end;   
  164.   if p = nil then  {After one list is exhausted, attach the remainder of the other one.}
  165.     s^.next := q
  166.   else
  167.     s^.next := p
  168. end;                                     {procedure Merge}
  169.  
  170. {Include the procedures MergeSort and Divide from Chapter 7 here.}
  171.  
  172. procedure MainMergeSort(var L: list);
  173. { Main procedure to invoke recursive procedure MergeSort, as listed 
  174.   in the text.  }
  175. begin
  176.   MergeSort(L.head)
  177. end;
  178. procedure InitializeList(var L: list);
  179. begin
  180.   L.head := nil
  181. end;
  182.  
  183.  
  184. procedure Insert(x: reference; var L: list);
  185. { Inserts the reference into the hash table of references.  }
  186. var
  187.   done:  Boolean;
  188.   p:  pointer;
  189.   q:  pointref;
  190. begin                                 {procedure Insert}
  191.   done := false;
  192.   p := L.head;
  193.   while (p <> nil) and (not done) do
  194.   begin
  195.     if p^.wd = x.wd then     {The word is already in the list, update its node.}
  196.     begin
  197.       p^.ct := p^.ct + 1;
  198.       new(q);
  199.       q^.pg := x.pg;
  200.       q^.next := p^.ref;
  201.       p^.ref := q;
  202.       done := true
  203.     end
  204.     else
  205.       p := p^.next
  206.   end;
  207.   if not done then
  208.   begin            {Insert a new entry if the word is not already in the table.}
  209.     p := nil;
  210.     new(p);
  211.     p^.wd := x.wd;
  212.     p^.ct := 1;                 {Initialize the count and the page references.}
  213.     new(q);
  214.     q^.pg := x.pg;
  215.     q^.next := nil;
  216.     p^.ref := q;
  217.     p^.next := L.head;
  218.     L.head := p
  219.   end
  220. end;                                  {procedure Insert}
  221.  
  222.  
  223. procedure Append(p: pointer; var L: list);
  224. { Append the nodes pointed to by p to the end of the list L.  }
  225. var
  226.   q: pointer;
  227. begin                                {procedure Append}
  228.   q := L.head;
  229.   if q = nil then        {The list is empty, make p the beginning of the list.}
  230.     L.head := p
  231.   else begin
  232.     while q^.next <> nil do          {Find the end of the list.}
  233.       q := q^.next;
  234.     q^.next := p
  235.   end
  236. end;                                 {procedure Append}
  237.  
  238.  
  239. procedure Place(var F: fileref; var L: list);
  240. { Places the words in file F into the list of words.  }
  241. var
  242.   x:  reference;
  243.   temp:  pointer;
  244. begin                            {procedure Place}
  245.   temp := L.head;         {Save the words that have already been processed.}
  246.   L.head := nil;
  247.   reset(F);
  248.   while not eof(F) do              {Insert all the words into the list.}
  249.   begin
  250.     x := F^;
  251.     get(F);
  252.     Insert(x, L)
  253.   end;
  254.   MainMergeSort(L);      {Sort the list and append the other work to the list.}
  255.   Append(temp, L)
  256. end;                             {procedure Place}
  257.  
  258.  
  259. procedure RemoveFirst(var p: pointer; var L: list);
  260. { Removes the first node from the list L.  }
  261. begin                                     {procedure RemoveFirst}
  262.   p := L.head;
  263.   if not Empty(L) then       {standard list operation}
  264.   begin
  265.     L.head := L.head^.next;
  266.     p^.next := nil
  267.   end
  268. end;                                      {procedure RemoveFirst}
  269.  
  270.  
  271. procedure ReadReference(var r: pointer; var F: text);
  272. { Reads reference from the file F.  }
  273. var
  274.   k:  char;
  275. begin                                  {procedure ReadReference}
  276.   if eof(F) then
  277.     r := nil
  278.   else begin
  279.     ReadWord(F, r^.wd);
  280.     readln(F, k);
  281.     case  k  of
  282.       'F', 'f':  r^.kind := hash;
  283.       'C', 'c':  begin
  284.               r^.kind := count;
  285.               r^.ct := 0
  286.              end;
  287.       'I', 'i':  begin
  288.              r^.kind := index;
  289.              r^.ref := nil
  290.             end
  291.     end
  292.   end
  293. end;                                   {procedure ReadReference}
  294.  
  295.  
  296. procedure WriteReference(p: pointer;  var NewIndex, NewHashFile: text);
  297. {writes a word to the appropriate file with the appropriate associated information}
  298. var
  299.   q:  pointref;
  300. begin                                 {procedure WriteReference}
  301.   with p^ do
  302.     case kind of
  303.       hash: begin                           {Write the word to the hash file.}
  304.              WriteWord(NewHashFile, wd);
  305.              writeln(NewHashFile)
  306.            end;
  307.       count:begin   {Write the word and its frequency to the new index file.}
  308.               WriteWord(NewIndex, wd);
  309.               write(NewIndex, 'c');
  310.               writeln(NewIndex, ct:5)
  311.             end;
  312.       index:begin   {Write the word and its page numbers to the new index file.}
  313.               WriteWord(NewIndex, wd);
  314.               write(NewIndex, 'i');
  315.               q := ref;
  316.               while q <> nil do
  317.               begin
  318.                 write(NewIndex, q^.pg:5);
  319.                 q := q^.next
  320.               end;
  321.               writeln(NewIndex)
  322.             end
  323.     end
  324. end;                                  {procedure WriteReference}
  325.  
  326.  
  327. procedure GetWordType(p: pointer);
  328. { Request the user to specify the category of the given word.  }
  329. var
  330.   response: char;
  331. begin                       {procedure GetWordType}
  332.   with p^ do
  333.   begin
  334.     repeat
  335.       WriteWord(output, wd);
  336.       write(' is (F, C, I)?');
  337.       readln(response)
  338.     until response in ['F', 'f', 'C', 'c', 'I', 'i'];
  339.     case response of
  340.       'F', 'f': kind := hash;
  341.       'C', 'c': kind := count;
  342.       'I', 'i': kind := index
  343.     end
  344.   end
  345. end;                        {procedure GetWordType}
  346.  
  347.  
  348. procedure Delete(var p: pointer);
  349. { Delete the word p^ as well as all of the page references associated with it.  }
  350. var
  351.   q, r:  pointref;
  352. begin                                 {procedure Delete}
  353.   if p^.kind = index then
  354.   begin
  355.     q := p^.ref;
  356.     while q <> nil do
  357.     begin       {Dispose the list of page references associated with the word.}
  358.       r := q^.next;
  359.       dispose(q);
  360.       p^.ref := r;
  361.       q := r
  362.     end
  363.   end;
  364.   dispose(p)            {dispose the node itself}
  365. end;                                  {procedure Delete}
  366.  
  367.  
  368. procedure CompareAndMerge(var L: list; var InIndex, NewIndex, NewHashFile: text);
  369. { Compare the list L with InIndex, merge if was found.  }
  370. var
  371.   p, r:  pointer;
  372. begin                              {procedure CompareAndMerge}
  373.   RemoveFirst(p, L);
  374.   new(r);
  375.   ReadReference(r, InIndex);
  376.   while p <> nil do
  377.     if r = nil then
  378.     begin
  379.       GetWordType(p);
  380.       WriteReference(p, NewIndex, NewHashFile);
  381.       Delete(p);             {Remove reference list and node from memory.}
  382.       RemoveFirst(p, L)
  383.     end
  384.     if p^.wd < r^.wd then
  385.     begin
  386.       GetWordType(p);
  387.       WriteReference(p, NewIndex, NewHashFile);
  388.       Delete(p);             {Remove reference list and node from memory.}
  389.       RemoveFirst(p, L)
  390.     end
  391.     else if p^.wd > r^.wd then  {Do not write a word that is not used to NewIndex.}
  392.       ReadReference(r, InIndex)
  393.     else begin {p^.wd = r^.wd}
  394.       p^.kind := r^.kind;
  395.       WriteReference(p, NewIndex, NewHashFile);
  396.       Delete(p);             {Remove reference list and node from memory.}
  397.       RemoveFirst(p, L);
  398.       ReadReference(r, InIndex)
  399.     end
  400. end;                               {procedure CompareAndMerge}
  401.  
  402.  
  403. begin                            {procedure ClassifyWords}
  404.   reset(InIndex);
  405.   rewrite(NewIndex);
  406.   rewrite(NewHashFile);
  407.    InitializeList(NewList);
  408.   for code := nfiles downto 1 do   {Place the words from each file into the list.}
  409.     Place(RefFile[code], NewList);
  410.   if not Empty(NewList) then       {standard list operation}
  411.     CompareAndMerge(NewList, InIndex, NewIndex, NewHashFile)
  412. end;                             {procedure ClassifyWords}
  413.  
  414.  
  415.  
  416.  
  417.  
  418. {Exercise E4}
  419.  
  420. procedure ClassifyWords;
  421. {The references stored in the temporary files are placed in a new hash table,
  422.  the words from the file InIndex are compared with the words in the new table
  423.  as they are merged into the file NewIndex.}
  424. const
  425.   RefTableSize = 3023;   {Size of the hash table to temporarily store words.}
  426.   RefTableMax = 3022;
  427. type
  428.   wordtype  = (hash, count, index);          {ways to process a word}
  429.   pointref  = ^reflist;
  430.   reflist = record                            {list of page references}
  431.             pg:   integer;
  432.             next: pointref
  433.           end;
  434.   pointer   = ^node;
  435.   node = record                     {Node of list storing words.}
  436.           wd:       word;
  437.           kind:     wordtype;
  438.           ct:       integer;
  439.           ref:      pointref;
  440.           next:     pointer
  441.          end;
  442.        {Cannot use varying types as wordtype is not known upon first reading.}
  443.   list = record
  444.          head:  pointer
  445.        end;
  446.   RefHashTable = array[0..RefTableMax] of list;
  447. var
  448.   code:       filecode;       {index used to loop through temporary files}
  449.   RefTable:   RefHashTable;      {stores all references in memory}
  450.   NewList:    list;
  451.  
  452.  
  453. procedure InitializeTable(var RefTable: RefHashTable);
  454. var  i:  integer;
  455. begin                           {procedure InitializeTable}
  456.   for i := 0 to RefTableMax do
  457.     RefTable[i].head := nil
  458. end;                            {procedure InitializeTable}
  459.  
  460.  
  461. function RefTableAddress(x: reference): integer;
  462. { Returns hashed address of reference.  }
  463. var
  464.   i, h:  integer;
  465. begin                                 {function Hash}
  466.   h := 0;
  467.   with x do
  468.     for i := 1 to maxwd do
  469.       h := h + ord(wd[i]);
  470.   RefTableAddress := h mod RefTableSize
  471. end;                                  {function Hash}
  472.  
  473.  
  474. procedure Insert(x: reference; pos: integer; var RefTable: RefHashTable);
  475. {inserts the reference into the hash table of references}
  476. var
  477.   done:  Boolean;
  478.   p:  pointer;
  479.   q:  pointref;
  480. begin                                 {procedure Insert}
  481.   done := false;
  482.   p := RefTable[pos].head;
  483.   while (p <> nil) and (not done) do
  484.   begin              {Search for the word, update the reference if it is found.}
  485.     if p^.wd = x.wd then begin
  486.       p^.ct := p^.ct + 1;                  {Update count and page reference.}
  487.       new(q);
  488.       q^.pg := x.pg;
  489.       q^.next := p^.ref;
  490.       p^.ref := q;
  491.       done := true
  492.     end
  493.     else
  494.       p := p^.next
  495.   end;
  496.   if not done then begin  {Insert a new entry if the word is not in the table.}
  497.     p := nil;
  498.     new(p);
  499.     p^.wd := x.wd;
  500.     p^.ct := 1;                 {Initialize the count and the page references.}
  501.     new(q);
  502.     q^.pg := x.pg;
  503.     q^.next := nil;
  504.     p^.ref := q;
  505.     p^.next := RefTable[pos].head;
  506.     RefTable[pos].head := p
  507.   end
  508. end;                                  {procedure Insert}
  509.  
  510.  
  511. procedure Place(var F: fileref; var RefTable: RefHashTable);
  512. {places the words in file F into the reference table}
  513. var
  514.   x:  reference;
  515. begin                            {procedure Place}
  516.   reset(F);
  517.   while not eof(F) do begin
  518.     x := F^;
  519.     get(F);
  520.     Insert(x, RefTableAddress(x), RefTable)
  521.   end
  522. end;                             {procedure Place}
  523.  
  524.  
  525. procedure LinkEntries(var RefTable: RefHashTable;  var NewList: list);
  526. { The references in the hashed table are combined into the list NewList.  }
  527. var
  528.   i: integer;
  529.   p: pointer;
  530. begin                            {procedure LinkEntries}
  531.   i := 0;
  532.   while (i < RefTableMax) and Empty(RefTable[i]) do  {Find the first entry.}
  533.     i := i + 1;
  534.   if i <= RefTableMax then
  535.   begin
  536.     NewList.head := RefTable[i].head; {Initialize the list to point to the first entry.}
  537.     p := RefTable[i].head;
  538.     if p <> nil then                 {Find the end of the first chain.}
  539.       while p^.next <> nil do
  540.         p := p^.next;
  541.     while (i < RefTableMax) do        {Link remaining entries into the list.}
  542.     begin
  543.       i := i + 1;
  544.       if not Empty(RefTable[i]) then     {standard list procedure}
  545.       begin
  546.         p^.next := RefTable[i].head;
  547.         while p^.next <> nil do    {Move p to the end of the chain.}
  548.           p := p^.next
  549.       end
  550.     end
  551.   end
  552.   else
  553.     NewList.head := nil
  554. end;                             {procedure LinkEntries} 
  555.  
  556.  
  557. {See the previous exercise for the following procedures.}
  558. procedure RemoveFirst(var p: pointer; var L: list);
  559. procedure ReadReference(var r: pointer; var F: text);
  560. procedure WriteReference(p: pointer;  var NewIndex, NewHashFile: text);
  561. procedure GetWordType(p: pointer);
  562. procedure Delete(var p: pointer);
  563. procedure CompareAndMerge(var L: list; var InIndex, NewIndex, NewHashFile: text);
  564. procedure Merge(p, q: pointer; var r: pointer);
  565. {Include the procedures MergeSort and Divide from Chapter 7 here.}
  566. procedure MainMergeSort(var L: list);
  567.  
  568.  
  569. begin                            {procedure ClassifyWords}
  570.   reset(InIndex);
  571.   rewrite(NewIndex);
  572.   rewrite(NewHashFile);
  573.   InitializeTable(RefTable);
  574.   for code := 1 to nfiles do    {Place all the words into the reference hash table.}
  575.     Place(RefFile[code], RefTable);
  576.   LinkEntries(RefTable, NewList);   {Link the entries of the table into a list.}
  577.   MainMergeSort(NewList);
  578.   if not Empty(NewList) then
  579.     CompareAndMerge(NewList, InIndex, NewIndex, NewHashFile)
  580. end;                             {procedure ClassifyWords}
  581.  
  582.